﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Drawing2D;
using Resource;

namespace OOO.Scene
{
    public class OOOWorld
    {
		private Level _level = null;

		private Graphics _g = null;

		public OOOWorld(Graphics g)
		{
			_g = g;

			_level = Level.Instance;

			RenderManager.Initailize(_g, 800, 430);
		}

		public void Update(int ms)
		{
			_level.Update(ms);
		}

		public void Render()
		{
			_level.Render();

			RenderManager.Instance.Render();
		}
    }

	public class Level
	{
		private static Level _instance;

		public static Level Instance { get { return _instance; } }

		public static void CreateLevel()
		{
 			_instance = new Level();
		}

		private List<GameEntity> _gameEntities = new List<GameEntity>();

		private List<GameEntity> _toRemoveEntities = new List<GameEntity>();

		private List<GameEntity> _toAddEntities = new List<GameEntity>();

		public bool IsGameOver { get; set; }

		private Actor _actor = null;

		public Level()
		{
			var skyCanvas = new Canvas(0, 0, 800, 150);
			var sky = new Sky(skyCanvas);
			var forrest = new Forrest(new Canvas(0, 150, 800, 130));
			var fps = new FPSCounter(new Canvas(0, 0, 50, 30));

			var cc = new Canvas(0, 280, 800, 150);
			var mud = new Mud(cc);
			_actor = new Actor(cc);

			_gameEntities.Add(sky);
			_gameEntities.Add(forrest);
			_gameEntities.Add(mud);
			_gameEntities.Add(fps);
			mud.Actor = _actor;
			_gameEntities.Add(_actor);
			_actor.X = 400;
			_actor.Y = 75;

			var zombieManager = new ZombieManager();
			zombieManager.ZombieCreatedEvent += z => {
				_toAddEntities.Add(z);
				HitTestManager.Instance.AddTestee(z);
			};

			zombieManager.Initialize(cc);

			BloodStrip strip1 = new BloodStrip(skyCanvas, Color.Red);
			BloodStrip strip2 = new BloodStrip(skyCanvas, Color.Blue);
			BloodStripManager.Instance.ActorBloodStrip = strip1;
			BloodStripManager.Instance.NPCBloodStrip = strip2;

			strip1.MaxVolume = _actor.MaxHP;
			strip1.CurrentVolume = _actor.HP;
			strip1.X = 30;
			strip1.Y = 5;

			strip2.X = strip2.Canvas.Width - strip2.Width - 30;
			strip2.Y = 5;
			_gameEntities.Add(BloodStripManager.Instance);
			_gameEntities.Add(zombieManager);


			var htm = HitTestManager.Instance;
			htm.AddTestee(_actor);
		}

		public void Update(int ms)
		{
			foreach (var entity in _gameEntities)
			{
				entity.Update(ms);
			}

			foreach (var entity in _toRemoveEntities)
			{
				_gameEntities.Remove(entity);
			}

			_toRemoveEntities.Clear();

			foreach (var entity in _toAddEntities)
			{
				_gameEntities.Add(entity);
			}

			_toAddEntities.Clear();

			if (_actor.HP <= 0)
				IsGameOver = true;
		}

		public void RemoveEntity(GameEntity ge)
		{
			_toRemoveEntities.Add(ge);

			if (ge is CollidableGameEntity)
				HitTestManager.Instance.RemoveTestee(ge as CollidableGameEntity);
		}

		public void Render()
		{
			foreach (var entity in _gameEntities)
			{
				entity.Render();
			}
		}
	}

	//相对位置到窗口客户区的映射
	public class Canvas
	{
		private int _width = 0;
		private int _height = 0;
		private int _x = 0;
		private int _y = 0;

		public int Width
		{
			get { return _width; }
		}

		public int Height
		{
			get { return _height; }
		}

		public Canvas(int x, int y, int w, int h)
		{
			_width = w;
			_x = x;
			_y = y;
			_height = h;
		}

		public void DrawBitmap(Bitmap bitmap, Rectangle dest)
		{
			dest.X += _x;
			dest.Y += _y;

			RenderManager.Instance.DrawBitmap(bitmap, dest);
		}

		public void DrawBitmap(Bitmap bitmap, Rectangle dest, Rectangle src)
		{
			dest.X += _x;
			dest.Y += _y;

			RenderManager.Instance.DrawBitmap(bitmap, dest, src);
		}

		public void DrawString(string text, Font font, Brush brush, RectangleF dest)
		{
			dest.X += _x;
			dest.Y += _y;

			RenderManager.Instance.DrawString(text, font, brush, dest);
		}

		public void DrawRect(Rectangle rect, Pen p)
		{
			rect.X += _x;
			rect.Y += _y;

			RenderManager.Instance.DrawRect(rect, p);
		}

		public void DrawRect(Rectangle rect, Brush b)
		{
			rect.X += _x;
			rect.Y += _y;

			RenderManager.Instance.DrawRect(rect, b);
		}

		public SizeF MeasureString(string s, Font f)
		{
			return RenderManager.Instance.MeasureString(s, f);
		}
	}

	public abstract class GameEntity
	{
		//在CANVAS中的相对位置
		protected double _x;
		protected double _y;
		protected int _width;
		protected int _height;
		protected Bitmap _texture;
		protected Canvas _canvas;

		public double X
		{
			get { return _x; }
			set { _x = value; }
		}

		public double Y
		{
			get { return _y; }
			set { _y = value; }
		}

		public int Width
		{
			get { return _width; }
			set { _width = value; }
		}

		public int Height
		{
			get { return _height; }
			set { _height = value; }
		}

		public Bitmap Texture
		{
			get { return _texture; }
			set { _texture = value; }
		}
		//实际在客户区坐标的信息
		public Canvas Canvas
		{
			get { return _canvas; }
			set { _canvas = value; }
		}

		public abstract void Update(int elapsedMS);
		public abstract void Render();
	}

	public abstract class CollidableGameEntity : GameEntity
	{
		protected bool _isDead = false;
		protected Point _backupPosition;

		public event Action<CollidableGameEntity> CollisionHappenEvent;

		public bool IsAlive { get { return !_isDead; } }

		public bool HitTest()
		{
			var htm = HitTestManager.Instance;
			var testeeList = htm.GetTesteeList();

			bool ret = false;

			var collisionArea = new Rectangle((int)_x, (int)_y + _height - 10, _width, 10);

			foreach (var testee in testeeList)
			{
				if (testee != this && testee.IsAlive)
				{
					var collisionArea2 = new Rectangle((int)testee._x, (int)testee._y + testee._height - 10, testee._width, 10);
					if (htm.HitTest(collisionArea, collisionArea2))
					{
						if (CollisionHappenEvent != null)
							CollisionHappenEvent(testee);
						ret = true;
					}
				}
			}

			return ret;
		}

		public virtual void BackupPosition()
		{
			_backupPosition = new Point((int)_x, (int)_y);
		}

		public virtual void RestorePosition()
		{
			_x = _backupPosition.X;
			_y = _backupPosition.Y;
		}
	}

	public abstract class Background : GameEntity
	{
		public Background(Canvas c, Bitmap t)
		{
			Canvas = c;
			Texture = t;
		}
	}

	public class RepeatBackground : Background
	{
		public RepeatBackground(Canvas c, Bitmap r)
			: base(c, r)
		{
			_width = c.Width;
			_height = c.Height;
		}

		public override void Render()
		{
			//资源宽于窗口
			if (_width <= _texture.Width)
			{
				//跨画面，需要分成2部分来画
				if (_x + _width > _texture.Width)
				{
					int leftWidth = _texture.Width - (int)_x;
					int rightWidht = _width - leftWidth;
					Rectangle leftWindow = new Rectangle((int)_x, (int)_y, leftWidth, _height);
					Rectangle rightWindow = new Rectangle(0, (int)_y, rightWidht, _height);

					Rectangle leftDest = new Rectangle(0, (int)_y, leftWidth, _height);
					Rectangle rightDest = new Rectangle(leftWidth, (int)_y, rightWidht, _height);

					_canvas.DrawBitmap(_texture, leftDest, leftWindow);
					_canvas.DrawBitmap(_texture, rightDest, rightWindow);
				}
				else
				{
					Rectangle window = new Rectangle((int)_x, (int)_y, _width, _height);
					Rectangle dest = new Rectangle(0, 0, _width, _height);
					_canvas.DrawBitmap(_texture, dest, window);
				}
			}
			else
			{
				Rectangle destWindow;

				Rectangle firstWindow = new Rectangle((int)_x, (int)_y, _texture.Width - (int)_x, _texture.Height - (int)_y);
				destWindow = new Rectangle(0, (int)_y, _texture.Width - (int)_x, _texture.Height - (int)_y);
				_canvas.DrawBitmap(_texture, destWindow, firstWindow);

				int startX = _texture.Width - (int)_x;

				while (startX + _texture.Width < _width)
				{
					Rectangle fullWindow = new Rectangle(0, (int)_y, _texture.Width, _texture.Height);
					destWindow = new Rectangle(startX, (int)_y, _texture.Width, _texture.Height);
					_canvas.DrawBitmap(_texture, destWindow, fullWindow);

					startX += _texture.Width;
				}

				int remainWidth = _width - startX;
				Rectangle lastWindow = new Rectangle(0, (int)_y, remainWidth, _texture.Height);
				destWindow = new Rectangle(startX, (int)_y, remainWidth, _texture.Height);
				_canvas.DrawBitmap(_texture, destWindow, lastWindow);
			}
		}

		public override void Update(int elapsedMS)
		{
			
		}

		public void Move(double deltaX, double deltaY)
		{
			_x += deltaX;
			_y += deltaY;

			if (_x < 0)
				_x += _texture.Width;
			if (_y < 0)
				_y += _texture.Height;

			if (_x > _texture.Width)
				_x -= _texture.Width;
			if (_y > _texture.Height)
				_y -= _texture.Height;
		}
	}

	public class Sky : RepeatBackground
	{
		private double _speed = 0.001; //每秒1像素

		public Sky(Canvas c) 
			: base(c, null)
		{
			_texture = ResourceManager.GetBitmap("cloud");
		}

		public override void Update(int elapsedMS)
		{
			Move(_speed * elapsedMS, 0);
		}
	}

	public class Forrest : RepeatBackground
	{
		public Forrest(Canvas c) 
			: base(c, null)
		{
			_texture = ResourceManager.GetBitmap("forrest");
		}
	}

	public class Mud : RepeatBackground
	{
		private const int TOTAL_LENGTH = 2000;
		private readonly int _leftLimit = 0;
		private readonly int _rightLimit = 0;

		public Actor Actor { get; set; }

		public Mud(Canvas c) 
			: base(c, null)
		{
			_texture = ResourceManager.GetBitmap("mud");
			_leftLimit = c.Width / 4;
			_rightLimit = _leftLimit * 3;
		}

		public override void Render()
		{
			base.Render();
		}

		public override void Update(int elapsedMS)
		{
			//暂时不要卷屏效果
			//int deltaX = Actor.DeltaX;
			//if (_currentPos + Width > TOTAL_LENGTH)
			//	_currentPos = TOTAL_LENGTH - _width;

			//if (Actor.X > _rightLimit && _currentPos < TOTAL_LENGTH - _width)
			//{
			//	Actor.X = _rightLimit;
			//	Move(deltaX, 0);

			//	_currentPos += deltaX;
			//}
			//else if (Actor.X < _leftLimit && _currentPos > 0)
			//{
			//	Actor.X = _leftLimit;
			//	Move(deltaX, 0);

			//	_currentPos += deltaX;
			//}
		}
	}

	public class FPSCounter : GameEntity
	{
		private DateTime _lastTime = DateTime.Now;
		private int _frameCount = 0;
		private double _elapsed = 0;
		private Font _font = new Font("雅黑", 12);
		private Brush _brush = new SolidBrush(Color.Black);
		private RectangleF _dest;
		private int _fps = 0;

		public FPSCounter(Canvas c)
		{
			_x = 0;
			_y = 0;
			_canvas = c;
			_dest.X = 0;
			_dest.Y = 0;
			_dest.Width = c.Width;
			_dest.Height = c.Height;
		}

		public override void Update(int elapsedMS)
		{
		}

		public override void Render()
		{
			DateTime now = DateTime.Now;
			_elapsed += (now - _lastTime).TotalMilliseconds;
			_frameCount++;

			if (_elapsed >= 1000)
			{
 				//draw text
				_fps = (int)(_frameCount * 1000 / _elapsed);

				_lastTime = now;
				_frameCount = 0;
				_elapsed = 0;
			}

			_canvas.DrawString(_fps.ToString(), _font, _brush, _dest);
		}
	}

	public class RenderManager
	{
		public static RenderManager Instance = null;
		public static void Initailize(Graphics realG, int w, int h)
		{
			if (Instance == null)
			{
				Instance = new RenderManager(realG, w, h);
			}
		}

		private DoubleBuffer _db = null;
		private Graphics _realG = null;
		private int _width = 0;
		private int _height = 0;

		private RenderManager(Graphics realG, int w, int h)
		{
			_realG = realG;
			_width = w;
			_height = h;

			_db = new DoubleBuffer(w, h);
		}

		public void DrawBitmap(Bitmap bitmap, Rectangle dest, bool doubleBuffer = true)
		{
			if (doubleBuffer)
			{
				_db.DrawBitmap(bitmap, dest);
			}
			else
			{
				_realG.DrawImage(bitmap, dest);
			}
		}

		public void DrawBitmap(Bitmap bitmap, Rectangle dest, Rectangle src, bool doubleBuffer = true)
		{
			if (doubleBuffer)
			{
				_db.DrawBitmap(bitmap, dest, src);
			}
			else
			{
				_realG.DrawImage(bitmap, dest, src, GraphicsUnit.Pixel);
			}
		}

		public void DrawString(string text, Font font, Brush brush, RectangleF rect, bool doubleBuffer = true)
		{
			if (doubleBuffer)
				_db.DrawString(text, font, brush, rect);
			else
				_realG.DrawString(text, font, brush, rect);
		}

		public void DrawRect(Rectangle rect, Pen p, bool doubleBuffer = true)
		{
			if (doubleBuffer)
				_db.DrawRect(rect, p);
			else
				_realG.DrawRectangle(p, rect);
		}

		public void DrawRect(Rectangle rect, Brush b, bool doubleBuffer = true)
		{
			if (doubleBuffer)
				_db.DrawRect(rect, b);
			else
				_realG.FillRectangle(b, rect);
		}

		public SizeF MeasureString(string s, Font f)
		{
			return _realG.MeasureString(s, f);
		}

		public void Render()
		{
			_realG.DrawImage(_db.Buffer, 0, 0);
		}
	}

	public class DoubleBuffer
	{
		private Bitmap _buff = null;
		private Graphics _g = null;

		private int _width = 0;
		private int _height = 0;

		public Bitmap Buffer { get { return _buff; } }

		public DoubleBuffer(int w, int h)
		{
			_width = w;
			_height = h;
			_buff = new Bitmap(w, h);
			_g = Graphics.FromImage(_buff);
		}

		public void DrawBitmap(Bitmap bitmap, Rectangle dest)
		{
			_g.DrawImage(bitmap, dest);
		}

		public void DrawBitmap(Bitmap bitmap, Rectangle dest, Rectangle src)
		{
			_g.DrawImage(bitmap, dest, src, GraphicsUnit.Pixel);
		}

		public void DrawString(string text, Font font, Brush brush, RectangleF rect)
		{
			_g.DrawString(text, font, brush, rect); 
		}

		public void DrawRect(Rectangle rect, Pen p)
		{
			_g.DrawRectangle(p, rect);
		}

		public void DrawRect(Rectangle rect, Brush b)
		{
			_g.FillRectangle(b, rect);
		}
	}

	public class Event
	{
		private DateTime _arriveTime;

		public DateTime ArriveTime
		{
			get
			{
				return _arriveTime;
			}
			set
			{
				_arriveTime = value;
			}
		}

		public Event(DateTime time)
		{
			_arriveTime = time;
		}
	}

	public enum KEY { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_SPACE, KEY_ENTER, KEY_A };
	public enum KEY_TYPE { KEY_DOWN, KEY_UP };

	public class KeyboardEvent : Event
	{
		public KeyboardEvent(KEY k, KEY_TYPE t, int c, DateTime time)
			: base(time)
		{
			this.Key = k;
			this.Type = t;
			this.Count = c;
		}

		public KEY Key { get; set; }
		public KEY_TYPE Type { get; set; }
		public int Count { get; set; }
	}

	public class KeyState
	{
		private TimeSpan _interval = TimeSpan.FromMilliseconds(200);

		public KEY Value { get; set; }

		public bool IsHeld { get; set; }
		public DateTime LastUpTime { get; set; }
		public DateTime LastDownTime { get; set; }
		public DateTime CurrentUpTime { get; set; }
		public DateTime CurrentDownTime { get; set; }
		public int Count { get; set; }

		public bool IsDoubleClicked 
		{
			get
			{
				if (CurrentUpTime - LastUpTime <= _interval && DateTime.Now - CurrentUpTime <= _interval)
					return true;
				else
					return false;
			}
		}

		public bool IsClicked
		{
			get
			{
				if (DateTime.Now - CurrentUpTime <= _interval)
					return true;
				else
					return false;
			}
		}

		public KeyState(KEY value)
		{
			IsHeld = false;
			LastUpTime = new DateTime(0);
			LastDownTime = new DateTime(0);
			CurrentDownTime = new DateTime(0);
			CurrentUpTime = new DateTime(0);
			Count = 0;
		}

		public void ChangeKeyState(KeyboardEvent e)
		{
			if (e.Type == KEY_TYPE.KEY_DOWN)
			{
				LastDownTime = CurrentDownTime;
				CurrentDownTime = e.ArriveTime;

				IsHeld = true;
			}
			else if (e.Type == KEY_TYPE.KEY_UP)
			{
				LastUpTime = CurrentUpTime;
				CurrentUpTime = e.ArriveTime;

				IsHeld = false;
			}

			Count = e.Count;
		}
	}

	public class KeyboardManager
	{
		private Dictionary<KEY, KeyState> _keys = new Dictionary<KEY, KeyState>();
		private static KeyboardManager _instance = new KeyboardManager();

		private string _inputAlphabet = string.Empty;
		private bool _enterSwitch = false;

		public static KeyboardManager Instance
		{
			get { return _instance; }
		}

		public KeyState GetKeyState(KEY key)
		{
			KeyState ret = null;
			if (_keys.ContainsKey(key))
				ret = _keys[key];
			else
			{
				ret = new KeyState(key);
				_keys[key] = ret;
			}
			
			return ret;
		}

		public void ChangeKeyState(KeyboardEvent e)
		{
			if (_keys.ContainsKey(e.Key))
				_keys[e.Key].ChangeKeyState(e);
			else
			{
				_keys[e.Key] = new KeyState(e.Key);
			}
		}

		public void SwitchEnter()
		{
			_enterSwitch = !_enterSwitch;

			if (_enterSwitch)
			{
				_inputAlphabet = string.Empty;
			}
		}

		public void AddAlphabet(int val)
		{
			if (_enterSwitch)
				_inputAlphabet += (char)val;
		}

		public string GetAlphabet()
		{
 			return _inputAlphabet;
		}

		public bool GetEnterSwitch()
		{
			return _enterSwitch;
		}
	}

	public class Actor : CollidableGameEntity
	{
		private Bitmap _standard = null;
		private Bitmap _run = null;

		private Animation _rollRightAnimation = null;
		private Animation _rollLeftAnimation = null;
		private Animation _jumpAnimation = null;
		private Animation _jumpRightAnimation = null;
		private Animation _jumpLeftAnimation = null;

		private Animation _attackRight = null;
		private Animation _attackLeft = null;

		private ChatBubble _chatBubble = null;

		private bool _dirRight = true;//人物朝向

		private static PointF[] s_jumpOffset = null;
		private static PointF[] s_jumpLeftOffset = null;
		private static PointF[] s_jumpRightOffset = null;
		private static PointF[] s_attackOffset = null;
		private static PointF[] s_rollLeft = null;
		private static PointF[] s_rollRight = null;

		static Actor()
		{
			s_rollLeft = new PointF[] { 
				new PointF(0, 0),
				new PointF(-3, 0),
				new PointF(-3, 0),
				new PointF(-3, 0),
				new PointF(-3, 0),
				new PointF(-3, 0),
				new PointF(-3, 0),
				new PointF(-2, 0),
			};

			s_rollRight = new PointF[] { 
				new PointF(0, 0),
				new PointF(3, 0),
				new PointF(3, 0),
				new PointF(3, 0),
				new PointF(3, 0),
				new PointF(3, 0),
				new PointF(3, 0),
				new PointF(6, 0),
			};

			s_jumpRightOffset = new PointF[] {
				new PointF(0, 0),
				new PointF(4, -5),
				new PointF(4, -5),
				new PointF(4, -5),
				new PointF(4, 5),
				new PointF(4, 5),
				new PointF(0, 5),
			};

			s_jumpOffset = new PointF[] {
				new PointF(0, 0),
				new PointF(0, -5),
				new PointF(0, -5),
				new PointF(0, -5),
				new PointF(0, 5),
				new PointF(0, 5),
				new PointF(0, 5)
			};

			s_jumpLeftOffset = new PointF[] {
				new PointF(0, 0),
				new PointF(-4, -5),
				new PointF(-4, -5),
				new PointF(-4, -5),
				new PointF(-4, 5),
				new PointF(-4, 5),
				new PointF(0, 5),
			};

			//由于图片宽度改变，这里调整一下，否则人会动
			s_attackOffset = new PointF[] 
			{
				new PointF(0, 0),
				new PointF(-4, 0),
 				new PointF(0, 0),
				new PointF(-4, 0),
				new PointF(0, 0),
				new PointF(0, 0)
			};
		}

		public Actor(Canvas c)
		{
			_canvas = c;
			_standard = ResourceManager.GetBitmap("actor2");
			_run = ResourceManager.GetBitmap("actor1");

			Utility.Alpha(_standard, Color.White.ToArgb());
			Utility.Alpha(_run, Color.White.ToArgb());

			_texture = _standard;

			_width = _texture.Width;
			_height = _texture.Height;

			//翻滚动画
			InitAnimation();

			//聊天气泡
			_chatBubble = new ChatBubble(_canvas);

			HP = 100;
			MaxHP = 100;
		}

		public int DeltaX { get; set; }
		public int DeltaY { get; set; }

		public int HP { get; set; }
		public int MaxHP { get; set; }

		private void InitAnimation()
		{
			var f0 = ResourceManager.GetBitmap("actor2");
			var f1 = ResourceManager.GetBitmap("actor_roll1");
			var f2 = ResourceManager.GetBitmap("actor_roll2");
			var f3 = ResourceManager.GetBitmap("actor_roll3");
			var f4 = ResourceManager.GetBitmap("actor_roll4");
			var f5 = ResourceManager.GetBitmap("actor_roll5");
			var f6 = ResourceManager.GetBitmap("actor_roll6");
			var f7 = f0;

			Utility.Alpha(f0, Color.White.ToArgb());
			Utility.Alpha(f1, Color.White.ToArgb());
			Utility.Alpha(f2, Color.White.ToArgb());
			Utility.Alpha(f3, Color.White.ToArgb());
			Utility.Alpha(f4, Color.White.ToArgb());
			Utility.Alpha(f5, Color.White.ToArgb());
			Utility.Alpha(f6, Color.White.ToArgb());

			Bitmap[] frames = new Bitmap[] { f7, f6, f5, f4, f3, f2, f1, f0 };

			_rollLeftAnimation = new Animation(_canvas, frames);
			_rollLeftAnimation.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				_rollLeftAnimation.X += s_rollLeft[index].X;
				_rollLeftAnimation.Y += s_rollLeft[index].Y;

				Move((int)s_rollLeft[index].X, (int)s_rollLeft[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};

			frames = new Bitmap[] { f0, f1, f2, f3, f4, f5, f6, f7 };

			_rollRightAnimation = new Animation(_canvas, frames);
			_rollRightAnimation.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				_rollRightAnimation.X += s_rollRight[index].X;
				_rollRightAnimation.Y += s_rollRight[index].Y;

				Move((int)s_rollRight[index].X, (int)s_rollRight[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};

			//jump
			frames = new Bitmap[] { f0, f0, f0, f0, f0, f0, f0 };

			_jumpAnimation = new Animation(_canvas, frames);
			_jumpAnimation.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				_jumpAnimation.X += s_jumpOffset[index].X;
				_jumpAnimation.Y += s_jumpOffset[index].Y;

				Move((int)s_jumpOffset[index].X, (int)s_jumpOffset[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};

			//jump right
			f1 = ResourceManager.GetBitmap("actor_jump1");
			f2 = ResourceManager.GetBitmap("actor_jump2");
			f3 = ResourceManager.GetBitmap("actor_jump3");
			f4 = ResourceManager.GetBitmap("actor_jump4");
			f5 = ResourceManager.GetBitmap("actor_jump5");

			Utility.Alpha(f1, Color.White.ToArgb());
			Utility.Alpha(f2, Color.White.ToArgb());
			Utility.Alpha(f3, Color.White.ToArgb());
			Utility.Alpha(f4, Color.White.ToArgb());
			Utility.Alpha(f5, Color.White.ToArgb());
			Utility.Alpha(f6, Color.White.ToArgb());

			frames = new Bitmap[] { f0, f1, f2, f3, f4, f5, f7 };

			_jumpRightAnimation = new Animation(_canvas, frames);
			_jumpRightAnimation.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				_jumpRightAnimation.X += s_jumpRightOffset[index].X;
				_jumpRightAnimation.Y += s_jumpRightOffset[index].Y;

				Move((int)s_jumpRightOffset[index].X, (int)s_jumpRightOffset[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};

			//jump left

			frames = new Bitmap[] { f7, f5, f4, f3, f2, f1, f0 };

			_jumpLeftAnimation = new Animation(_canvas, frames);
			_jumpLeftAnimation.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				_jumpLeftAnimation.X += s_jumpLeftOffset[index].X;
				_jumpLeftAnimation.Y += s_jumpLeftOffset[index].Y;

				Move((int)s_jumpLeftOffset[index].X, (int)s_jumpLeftOffset[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};

			//攻击
			f0 = ResourceManager.GetBitmap("actor_attack_right0");
			f1 = ResourceManager.GetBitmap("actor_attack_right1");
			f2 = ResourceManager.GetBitmap("actor_attack_right2");
			f3 = ResourceManager.GetBitmap("actor_attack_right3");

			Utility.Alpha(f0, Color.White.ToArgb());
			Utility.Alpha(f1, Color.White.ToArgb());
			Utility.Alpha(f2, Color.White.ToArgb());
			Utility.Alpha(f3, Color.White.ToArgb());

			frames = new Bitmap[] { f0, f1, f2, f3, f3, f3 };

			_attackRight = new Animation(_canvas, frames);
			_attackRight.AnimationStopEvent += () =>
			{
				var htm = HitTestManager.Instance;
				var testeeList = htm.GetTesteeList();

				var fistArea = new Rectangle((int)_attackRight.X + _attackRight.Width - 10, (int)_attackRight.Y + 20, 10, 10);

				foreach (var testee in testeeList)
				{
					if (testee is Zombie)
					{
						var halfBodyArea = new Rectangle((int)testee.X, (int)testee.Y, testee.Width, testee.Height / 2);
						if (htm.HitTest(fistArea, halfBodyArea))
						{
							var zombie = testee as Zombie;
							zombie.Hurt();
						}
					}
				}
			};

			f0 = ResourceManager.GetBitmap("actor_attack_left0");
			f1 = ResourceManager.GetBitmap("actor_attack_left1");
			f2 = ResourceManager.GetBitmap("actor_attack_left2");
			f3 = ResourceManager.GetBitmap("actor_attack_left3");

			Utility.Alpha(f0, Color.White.ToArgb());
			Utility.Alpha(f1, Color.White.ToArgb());
			Utility.Alpha(f2, Color.White.ToArgb());
			Utility.Alpha(f3, Color.White.ToArgb());

			frames = new Bitmap[] { f0, f1, f2, f3, f3, f3 };

			_attackLeft = new Animation(_canvas, frames);
			_attackLeft.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				_attackLeft.X += s_attackOffset[index].X;
				_attackLeft.Y += s_attackOffset[index].Y;

				Move((int)s_attackOffset[index].X, (int)s_attackOffset[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};

			_attackLeft.AnimationStopEvent += () =>
			{
				Move(8, 0);

				var htm = HitTestManager.Instance;
				var testeeList = htm.GetTesteeList();

				var fistArea = new Rectangle((int)_attackLeft.X, (int)_attackLeft.Y + 20, 10, 10);

				foreach (var testee in testeeList)
				{
					if (testee is Zombie)
					{
						var halfBodyArea = new Rectangle((int)testee.X, (int)testee.Y, testee.Width, testee.Height / 2);
						if (htm.HitTest(fistArea, halfBodyArea))
						{
							var zombie = testee as Zombie;
							zombie.Hurt();
						}
					}
				}
			};
		}

		private Point _chatBubbleBackupPosition = new Point();

		public override void BackupPosition()
		{
			base.BackupPosition();

			_chatBubbleBackupPosition.X = (int)_chatBubble.X;
			_chatBubbleBackupPosition.Y = (int)_chatBubble.Y;
		}

		public void Hurt()
		{
			HP--;

			if (HP < 0)
				HP = 0;

			BloodStripManager.Instance.ActorBloodStrip.CurrentVolume = HP;
		}

		public override void RestorePosition()
		{
			base.RestorePosition();

			_chatBubble.X = _chatBubbleBackupPosition.X;
			_chatBubble.Y = _chatBubbleBackupPosition.Y;
		}

		public override void Update(int elapsedMS)
		{
			_chatBubble.Update(elapsedMS);

			if (_attackLeft.IsRunning)
			{
				_attackLeft.Update(elapsedMS);
				return;
			}
			else if (_attackRight.IsRunning)
			{
				_attackRight.Update(elapsedMS);
				return;
			}
			if (_rollRightAnimation.IsRunning)
			{
				_rollRightAnimation.Update(elapsedMS);
				return;
			}
			else if (_rollLeftAnimation.IsRunning)
			{
				_rollLeftAnimation.Update(elapsedMS);
				return;
			}
			else if (_jumpAnimation.IsRunning)
			{
				_jumpAnimation.Update(elapsedMS);
				return;
			}
			else if (_jumpRightAnimation.IsRunning)
			{
				_jumpRightAnimation.Update(elapsedMS);
				return;
			}
			else if (_jumpLeftAnimation.IsRunning)
			{
				_jumpLeftAnimation.Update(elapsedMS);
				return;
			}

			var left = KeyboardManager.Instance.GetKeyState(KEY.KEY_LEFT);
			var right = KeyboardManager.Instance.GetKeyState(KEY.KEY_RIGHT);
			var space = KeyboardManager.Instance.GetKeyState(KEY.KEY_SPACE);
			var a = KeyboardManager.Instance.GetKeyState(KEY.KEY_A);

			if (a.IsClicked && !_dirRight)
			{
				_attackLeft.Start(200, (int)_x, (int)_y);
			}
			else if (a.IsClicked && _dirRight)
			{
				_attackRight.Start(200, (int)_x, (int)_y);
			}
			else if (right.IsHeld && space.IsClicked)
			{
				_jumpRightAnimation.Start(500, (int)_x, (int)_y);
				_dirRight = true;
			}
			else if (left.IsHeld && space.IsClicked)
			{
				_jumpLeftAnimation.Start(500, (int)_x, (int)_y);
				_dirRight = false;
			}
			else if (right.IsDoubleClicked)
			{
				_rollRightAnimation.Start(500, (int)_x, (int)_y);
				_dirRight = true;
			}
			else if (left.IsDoubleClicked)
			{
				_rollLeftAnimation.Start(500, (int)_x, (int)_y);
				_dirRight = false;
			}
			else if (space.IsClicked)
			{
				_jumpAnimation.Start(500, (int)_x, (int)_y);
			}
			else
			{
				DeltaX = 0;

				if (left.IsHeld)
					DeltaX -= 1 * left.Count;

				if (right.IsHeld)
					DeltaX = 1 * right.Count;

				var up = KeyboardManager.Instance.GetKeyState(KEY.KEY_UP);
				var down = KeyboardManager.Instance.GetKeyState(KEY.KEY_DOWN);

				DeltaY = 0;

				if (up.IsHeld)
					DeltaY -= 1 * up.Count;

				if (down.IsHeld)
					DeltaY = 1 * down.Count;

				if (DeltaX > 0)
					_dirRight = true;
				else if (DeltaX < 0)
					_dirRight = false;

				bool isMoving = (left.IsHeld && !right.IsHeld) || (right.IsHeld && !left.IsHeld) ||
								(up.IsHeld && !down.IsHeld) || (down.IsHeld && !up.IsHeld);

				if (isMoving)
				{
					if (_texture == _run)
						_texture = _standard;
					else
						_texture = _run;
				}
				else
				{
					_texture = _standard;
				}

				var oy = (int)_y;
				BackupPosition();

				Move(DeltaX, DeltaY);

				//计算碰撞
				if (HitTest())
				{
					RestorePosition();
				}
			}
		}

		public override void Render()
		{
			if (_attackLeft.IsRunning)
				_attackLeft.Render();
			else if (_attackRight.IsRunning)
				_attackRight.Render();
			else if (_rollRightAnimation.IsRunning)
				_rollRightAnimation.Render();
			else if (_rollLeftAnimation.IsRunning)
				_rollLeftAnimation.Render();
			else if (_jumpAnimation.IsRunning)
				_jumpAnimation.Render();
			else if (_jumpRightAnimation.IsRunning)
				_jumpRightAnimation.Render();
			else if (_jumpLeftAnimation.IsRunning)
				_jumpLeftAnimation.Render();
			else
				_canvas.DrawBitmap(_texture, new Rectangle((int)_x, (int)_y, _width, _height));

			_chatBubble.Render();
		}

		private void Move(int deltaX, int deltaY)
		{
			_x += deltaX;
			_y += deltaY;

			if (_x < 0)
				_x = 0;
			else if (_x > _canvas.Width - _width)
				_x = _canvas.Width - _width;

			if (_y < 0 - _height)
				_y = 0 - _height;
			else if (_y > _canvas.Height -_height)
				_y = _canvas.Height - _height;

			//更新气泡位置
			_chatBubble.X = _x + _width + 1;
			_chatBubble.Y = _y - 1;
		}
	}

	public class ChatBubble : GameEntity
	{
		private Font _font = new Font("雅黑", 8);
		private SolidBrush _brush = new SolidBrush(Color.Black);
		private Animation _bubblePopup = null;

		private const int Duration = 2000;
		private int _tillNow = 0;
		private bool _popupFinish = false;
		private bool _inputMode = false;

		public string Content { get; set; }

		private static PointF[] s_bubbleOffset = null;
		private static Bitmap[] s_frames = null;

		static ChatBubble()
		{
			var f0 = ResourceManager.GetBitmap("chatbubble0");
			var f1 = ResourceManager.GetBitmap("chatbubble1");
			var f2 = ResourceManager.GetBitmap("chatbubble2");
			var f3 = ResourceManager.GetBitmap("chatbubble3");

			Utility.Alpha(f0, Color.White.ToArgb());
			Utility.Alpha(f1, Color.White.ToArgb());
			Utility.Alpha(f2, Color.White.ToArgb());
			Utility.Alpha(f3, Color.White.ToArgb());

			s_frames = new Bitmap[] { f0, f1, f2, f3 };

			s_bubbleOffset = new PointF[] 
			{
				new PointF(0, -f0.Height),
				new PointF(1, -f1.Height - 1),
				new PointF(1, -f2.Height - 2),
				new PointF(1, -f3.Height - 3),
			};

			s_bubbleOffset[1].Y -= s_bubbleOffset[0].Y;
			s_bubbleOffset[2].Y -= s_bubbleOffset[1].Y;
			s_bubbleOffset[3].Y -= s_bubbleOffset[2].Y;
		}

		public ChatBubble(Canvas c)
		{
			_canvas = c;
			Content = string.Empty;

			_texture = s_frames[s_frames.Length - 1];

			_width = _texture.Width;
			_height = _texture.Height;

			_bubblePopup = new Animation(_canvas, s_frames);
			_bubblePopup.AnimationStopEvent += OnBubblePopupComplete;
			_bubblePopup.AnimationFrameChangeEvent += index =>
			{
				_bubblePopup.X += s_bubbleOffset[index].X;
				_bubblePopup.Y += s_bubbleOffset[index].Y;
			};
		}

		private void OnBubblePopupComplete()
		{
			_popupFinish = true;
		}

		public override void Update(int elapsedMS)
		{
			if (KeyboardManager.Instance.GetEnterSwitch() && !_inputMode)
			{
				_inputMode = true;
			}
			else if (!KeyboardManager.Instance.GetEnterSwitch() && _inputMode)
			{
				_inputMode = false;

				_popupFinish = false;
				_bubblePopup.Start(100, (float)_x, (float)_y);
			}
			else if (_popupFinish)
			{
				_tillNow += elapsedMS;
			}
			else if (_bubblePopup.IsRunning)
			{
				_bubblePopup.Update(elapsedMS);
			}
		}

		public override void Render()
		{
			if (_bubblePopup.IsRunning)
			{
				_bubblePopup.Render();
			}
			else if (_popupFinish && _tillNow <= Duration)
			{
				Rectangle bubbleRect = new Rectangle((int)_x, (int)_y - Height - 4, _width, _height);
				Rectangle textRect = new Rectangle((int)_x + 5, (int)_y - (int)((Height + _font.Size + 10) / 2) - 4, _width - 5, (int)_font.Size + 5);

				if (string.IsNullOrEmpty(Content))
				{
					Content = KeyboardManager.Instance.GetAlphabet();
					if (Content.Length > 4)
						Content = Content.Substring(0, 4);
				}

				_canvas.DrawBitmap(_texture, bubbleRect);
				_canvas.DrawString(Content, _font, _brush, textRect);
			}
			else
			{
				_tillNow = 0;
				_popupFinish = false;
				Content = string.Empty;
			}
		}
	}

	public class Zombie : CollidableGameEntity
	{
		private bool _left = false;
		private double _speed = 0.01; //每秒1像素
		private static Random Rand = new Random(DateTime.Now.Millisecond);

		private double _totalms = 0;
		private static Bitmap LeftZombie = null;
		private static Bitmap RightZombie = null;
		private static PointF[] s_leftZombieOffset = null;
		private static PointF[] s_rightZombieOffset = null;

		private Animation _jump = null;
		private Animation _dead = null;
		private BloodEffect _blood = null;
		private int _bloodOffsetX = 0;
		private int _bloodOffsetY = 0;
		private DateTime _lastAttackTime = DateTime.Now;
		private int _attackInterval = 1000;

		public int HP { get; set; }
		public int MaxHP { get; set; }

		static Zombie()
		{
			LeftZombie = ResourceManager.GetBitmap("zombie0");
			RightZombie = ResourceManager.GetBitmap("zombie1");

			Utility.Alpha(LeftZombie, Color.White.ToArgb());
			Utility.Alpha(RightZombie, Color.White.ToArgb());

			s_leftZombieOffset = new PointF[] { 
					new Point(0, 0),
					new Point(-3, -5),
					new Point(-3, -5),
					new Point(-3, 5),
					new Point(-3, 5),
				};

			s_rightZombieOffset = new PointF[] { 
					new Point(0, 0),
					new Point(3, -5),
					new Point(3, -5),
					new Point(3, 5),
					new Point(3, 5),
				};
		}

		public Zombie(Canvas c, bool left)
		{
			_canvas = c;
			_left = left;

			if (left)
			{
				_texture = LeftZombie;
			}
			else
			{
				_texture = RightZombie;
			}
			
			_width = _texture.Width;
			_height = _texture.Height;

			if (left)
			{
				_x = _canvas.Width - _width;
				_y = _canvas.Height - _height;
			}
			else
			{
				_x = 0;
				_y = _canvas.Height - _height;
			}

			InitJumpAnimation();

			_blood = new BloodEffect(_canvas);
			if (_left)
			{
				_bloodOffsetX = 5;
				_bloodOffsetY = 15;
			}
			else
			{
				_bloodOffsetX = _width - _blood.Width - 5;
				_bloodOffsetY = 15;
			}

			MaxHP = HP = 100;

			CollisionHappenEvent += cge => {
				if (cge is Actor)
				{
					DateTime now = DateTime.Now;
					if ((now - _lastAttackTime).TotalMilliseconds >= _attackInterval)
					{
						var actor = cge as Actor;
						actor.Hurt();

						_lastAttackTime = now;
					}
				}
			};
		}

		private void InitJumpAnimation()
		{
			Bitmap[] frames = null;
			
			if (_left)
			{
				frames = new Bitmap[] { LeftZombie, LeftZombie, LeftZombie, LeftZombie, LeftZombie };
			}
			else
			{
				frames = new Bitmap[] { RightZombie, RightZombie, RightZombie, RightZombie, RightZombie };
			}

			if (_jump != null)
				_jump.Stop();

			_jump = new Animation(_canvas, frames);
			_jump.AnimationFrameChangeEvent += index =>
			{
				BackupPosition();

				PointF[] offset = null;
				if (_left)
					offset = s_leftZombieOffset;
				else
					offset = s_rightZombieOffset;

				_jump.X += offset[index].X;
				_jump.Y += offset[index].Y;

				Move((int)offset[index].X, (int)offset[index].Y);

				if (HitTest())
				{
					RestorePosition();
				}
			};
		}

		private void InitDeadAnimation()
		{
			Bitmap[] frames = null;

			Bitmap f0 = null;
			Bitmap f1 = null;
			Bitmap f2 = null;
			Bitmap f3 = null;
			Bitmap f4 = null;
			Bitmap f5 = null;

			if (_left)
			{
				f0 = LeftZombie;
				f1 = Utility.Alpha((Bitmap)LeftZombie.Clone(), (byte)200);
				f2 = Utility.Alpha((Bitmap)LeftZombie.Clone(), (byte)150);
				f3 = Utility.Alpha((Bitmap)LeftZombie.Clone(), (byte)100);
				f4 = Utility.Alpha((Bitmap)LeftZombie.Clone(), (byte)5);
				f5 = Utility.Alpha((Bitmap)LeftZombie.Clone(), (byte)0);

				frames = new Bitmap[] { f0, f1, f2, f3, f4, f5 };
			}
			else
			{
				f0 = RightZombie;
				f1 = Utility.Alpha((Bitmap)RightZombie.Clone(), (byte)200);
				f2 = Utility.Alpha((Bitmap)RightZombie.Clone(), (byte)150);
				f3 = Utility.Alpha((Bitmap)RightZombie.Clone(), (byte)100);
				f4 = Utility.Alpha((Bitmap)RightZombie.Clone(), (byte)50);
				f5 = Utility.Alpha((Bitmap)RightZombie.Clone(), (byte)0);

				frames = new Bitmap[] { f0, f1, f2, f3, f4, f5 };
			}

			_dead = new Animation(_canvas, frames);

			_dead.AnimationStopEvent += () => {
				Level.Instance.RemoveEntity(this);
			};
		}

		public void Hurt()
		{
			HP -= 25;

			if (HP < 0)
				HP = 0;

			BloodStripManager.Instance.NPCBloodStrip.MaxVolume = MaxHP;
			BloodStripManager.Instance.NPCBloodStrip.CurrentVolume = HP;

			if (HP == 0)
			{
				_blood.Clear();

				InitDeadAnimation();

				_isDead = true;

				_dead.Start(1000, (int)_x, (int)_y);

				BloodStripManager.Instance.NPCBloodStrip.MaxVolume = -1;
				BloodStripManager.Instance.NPCBloodStrip.CurrentVolume = -1;

				ZombieManager.Instance.RemoveZombie(this);
			}
			else
			{
				_blood.AddBlood();
			}
		}

		public override void Render()
		{
			if (_dead != null && _dead.IsRunning)
				_dead.Render();
			else if (_jump.IsRunning)
				_jump.Render();
			else
				_canvas.DrawBitmap(_texture, new Rectangle((int)_x, (int)_y, _width, _height));

			_blood.Render();
		}

		public override void Update(int elapsedMS)
		{
			_totalms += elapsedMS;

			if (_dead != null && _dead.IsRunning)
				_dead.Update(elapsedMS);
			else if (_jump.IsRunning)
				_jump.Update(elapsedMS);
			else
			{
				int r = 0;

				if (_totalms >= 500)
				{
					_totalms = 0;

					r = Rand.Next(20);

					if (r < 1)
						TurnAround();

					r = Rand.Next(10);

					if (r < 5)
						_jump.Start(500, (int)_x, (int)_y);
				}
				else
				{
					double offset = elapsedMS * _speed;

					BackupPosition();

					Move(!_left ? offset : -offset, 0);
					if (HitTest())
					{
						RestorePosition();
					}
				}
			}

			_blood.Update(elapsedMS);
			_blood.X = _x + _bloodOffsetX;
			_blood.Y = _y + _bloodOffsetY;
		}

		private void Move(double offsetX, double offsetY)
		{
			_x += offsetX;
			_y += offsetY;

			if (_x > _canvas.Width - _width)
			{
				_x = _canvas.Width - _width;
				TurnAround();
			}
			else if (_x < 0)
			{
				_x = 0;
				TurnAround();
			}

			if (_y < 0)
				_y = 0;
			else if (_y > _canvas.Height - _height)
				_y = _canvas.Height - _height;
		}

		private void TurnAround()
		{
			_left = !_left;

			if (_left)
				_texture = LeftZombie;
			else
				_texture = RightZombie;

			InitJumpAnimation();
		}
	}

	public class ZombieManager : GameEntity
	{
		private static ZombieManager _instance = new ZombieManager();

		public static ZombieManager Instance { get { return _instance; } }

		private List<Zombie> _zombieList = new List<Zombie>();
		private List<Zombie> _toRemoveList = new List<Zombie>();
		private Random _rand = new Random(DateTime.Now.Millisecond);
		private DateTime _lastCreateTime = DateTime.Now;
		private int _createInterval = 5000;

		public void Initialize(Canvas c)
		{
			_canvas = c;

			CreateZombie();
			CreateZombie();
		}

		public List<Zombie> ZombieList { get { return _zombieList; } }
		public List<Zombie> RemovedZombieList { get { return _toRemoveList; } }

		public event Action<Zombie> ZombieCreatedEvent;

		public void CreateZombie(int x, int y, bool left)
		{
			Zombie z = new Zombie(_canvas, left);

			z.X = x;
			z.Y = y;

			_zombieList.Add(z);

			if (ZombieCreatedEvent != null)
				ZombieCreatedEvent(z);
		}

		public void CreateZombie()
		{
			bool left = _rand.Next(2) == 0 ? true : false;
			Zombie z = new Zombie(_canvas, left);

			int x = 0;
			int y = _rand.Next(_canvas.Height - z.Height);
			if (!left)
			{
				x = _canvas.Width - z.Width;
			}

			z.X = x;
			z.Y = y;

			_zombieList.Add(z);

			if (ZombieCreatedEvent != null)
				ZombieCreatedEvent(z);
		}

		public void RemoveZombie(Zombie z)
		{
			_toRemoveList.Add(z);
		}

		public override void Update(int elapsedMS)
		{
			DateTime now = DateTime.Now;
			if ((now - _lastCreateTime).TotalMilliseconds >= _createInterval)
			{
				CreateZombie();
				_lastCreateTime = now;
			}
		}

		public override void Render()
		{
		}
	}

	public class BloodEffect : GameEntity
	{
		private static Random Rand = new Random(DateTime.Now.Millisecond);
		private static int EachDuration = 2000;

		private List<DurationPoint> _positionList = new List<DurationPoint>();//相对位置,value是这个点的持续时间

		private class DurationPoint
		{
			public DurationPoint(Point p, int d)
			{
				Point = p;
				Duration = d;
			}
			public Point Point { get; set; }
			public int Duration { get; set; }
		}

		public BloodEffect(Canvas c)
		{
			_texture = ResourceManager.GetBitmap("blood");
			Utility.Alpha(_texture, Color.White.ToArgb());

			_canvas = c;
			_width = 12;
			_height = 12;
		}

		public void Clear()
		{
			_positionList.Clear();
		}

		public void AddBlood()
		{
			int x = Rand.Next(_width);
			int y = Rand.Next(_height);

			Point p = new Point(x, y);
			_positionList.Add(new DurationPoint(p, 0));
		}

		public override void Update(int elapsedMS)
		{
			List<DurationPoint> timeoverPoint = new List<DurationPoint>();
			foreach (var p in _positionList)
			{
				p.Duration += elapsedMS;

				if (p.Duration > EachDuration)
					timeoverPoint.Add(p);
			}

			foreach (var p in timeoverPoint)
			{
				_positionList.Remove(p);
			}
		}

		public override void Render()
		{
			foreach (var p in _positionList)
			{
 				_canvas.DrawBitmap(_texture, new Rectangle((int)_x + p.Point.X, (int)_y + p.Point.Y, _texture.Width, _texture.Height));
			}
		}
	}

	public class BloodStrip : GameEntity
	{
		private Pen _framePen = null;
		private Brush _emptyBrush = null;
		private Brush _bloodBrush = null;
		private Color _color = Color.Empty;

		private int _contentWidth = 0;
		private int _contentHeight = 0;

		public float MaxVolume { get; set; }
		public float CurrentVolume { get; set; }
		public float MinVolume { get; set; }

		public BloodStrip(Canvas c, Color color)
		{
			_canvas = c;
			_contentWidth = 200;
			_contentHeight = 15;
			_color = color;

			_framePen = new Pen(Color.Black);
			_emptyBrush = new SolidBrush(Color.White);

			int frameSize = (int)_framePen.Width;
			_width = _contentWidth + frameSize;
			_height = _contentHeight + frameSize;

			CurrentVolume = -1;
		}

		public override void Update(int elapsedMS)
		{
			
		}

		public override void Render()
		{
			Rectangle rect = new Rectangle((int)_x, (int)_y, _width, _height);
			_canvas.DrawRect(rect, _framePen);

			float ratio = CurrentVolume / MaxVolume;
			int bloodWidth = (int)(ratio * _contentWidth);
			int emptyWidth = _contentWidth - bloodWidth;
			int frameSize = (int)_framePen.Width;

			_bloodBrush = new LinearGradientBrush(new Point((int)_x, _height), new Point((int)_x + _width, (int)_y + _height), Color.White, _color);
			rect = new Rectangle((int)_x + frameSize, (int)_y + frameSize, bloodWidth, _contentHeight);
			_canvas.DrawRect(rect, _bloodBrush);

			rect = new Rectangle((int)_x + frameSize + bloodWidth, (int)_y + frameSize, emptyWidth, _contentHeight);
		}
	}

	public class BloodStripManager : GameEntity
	{
		private static BloodStripManager _instance = new BloodStripManager();

		public static BloodStripManager Instance { get { return _instance; } }

		public BloodStrip ActorBloodStrip { get; set; }
		public BloodStrip NPCBloodStrip { get; set; }

		public override void Update(int elapsedMS)
		{
			
		}

		public override void Render()
		{
			if (ActorBloodStrip != null && ActorBloodStrip.CurrentVolume >= 0)
				ActorBloodStrip.Render();

			if (NPCBloodStrip != null && NPCBloodStrip.CurrentVolume >= 0)
				NPCBloodStrip.Render();
		}
	}

	public class Utility
	{
		public static void Alpha(Bitmap b, int rgb)
		{
			var buff = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

			int stride = buff.Stride;
			
			unsafe
			{
				byte* pixel = (byte*)(void*)buff.Scan0;

				for (int y = 0; y < buff.Height; y++)
				{
					int yPos = y * stride;
					for (int x = 0; x < buff.Width; x++)
					{
						int pos = yPos + (x * 4);
						if (*(int*)(pixel + pos) == rgb)
							pixel[pos + 3] = 0;
					}
				}
			}

			b.UnlockBits(buff);
		}

		public static Bitmap Alpha(Bitmap b, byte alpha)
		{
			b = (Bitmap)b.Clone();
			var buff = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

			int stride = buff.Stride;

			unsafe
			{
				byte* pixel = (byte*)(void*)buff.Scan0;

				for (int y = 0; y < buff.Height; y++)
				{
					int yPos = y * stride;
					for (int x = 0; x < buff.Width; x++)
					{
						int pos = yPos + (x * 4);
						if (*(byte*)(pixel + pos + 3) != 0)
							pixel[pos + 3] = alpha;
					}
				}
			}

			b.UnlockBits(buff);

			return b;
		}
	}

	public class HitTestManager
	{
		private static HitTestManager _instance;
		private List<CollidableGameEntity> _testeeList = new List<CollidableGameEntity>();

		public static HitTestManager Instance
		{
			get
			{
				if (_instance == null)
					_instance = new HitTestManager();
				return _instance;
			}
		}

		public void AddTestee(CollidableGameEntity o)
		{
			_testeeList.Add(o);
		}

		public void RemoveTestee(CollidableGameEntity o)
		{
			_testeeList.Remove(o);
		}

		public List<CollidableGameEntity> GetTesteeList()
		{
			return _testeeList;
		}

		public bool HitTest(Rectangle a, Rectangle b)
		{
			var x1 = Math.Max(a.X, b.X);
			var y1 = Math.Max(a.Y, b.Y);
			var x2 = Math.Min(a.X + a.Width, b.X + b.Width);
			var y2 = Math.Min(a.Y + a.Height, b.Y + b.Height);

			if (x1 < x2 && y1 < y2)
				return true;
			else
				return false;
		}

		public bool HitTest(Point a, Rectangle b)
		{
			if (a.X > b.X && a.X < b.X + b.Width &&
				a.Y > b.Y && a.Y < b.Y + b.Height)
				return true;
			else
				return false;
		}
	}

	public class TextButton : GameEntity
	{
		public string Content { get; set; }

		public Font Font { get; set; }
		public Color FrameColor { get; set; }
		public Color TextColor { get; set; }
		public Color BackgroundColor { get; set; }

		private bool IsDown = false;

		public TextButton(Canvas c)
		{
			_canvas = c;
			Font = new Font("雅黑", 24);

			FrameColor = Color.Black;
			TextColor = Color.Black;
			BackgroundColor = Color.YellowGreen;
		}

		public event Action Clicked;

		public void OnMouseDown()
		{
			IsDown = true;
		}

		public void OnMouseUp()
		{
			if (Clicked != null)
				Clicked();

			IsDown = false;
		}

		public override void Update(int elapsedMS)
		{
		}

		public override void Render()
		{
			var pen = new Pen(FrameColor);
			int pSize = (int)pen.Width;
			int offest = 0;

			if (IsDown)
			{
				offest = 2;
			}

			_canvas.DrawRect(new Rectangle((int)_x + offest, (int)_y + offest, _width, _height), pen);
			_canvas.DrawRect(new Rectangle((int)_x + pSize + offest, (int)_y + pSize + offest, _width - pSize, _height - pSize), new SolidBrush(BackgroundColor));

			SizeF tSize = _canvas.MeasureString(Content, Font);
			int x = (int)(_width - 2 * pSize - tSize.Width) / 2;
			int y = (int)(_height - 2 * pSize - tSize.Height) / 2;

			if (x < 0)
				x = 0;
			if (y < 0)
				y = 0;

			x += (int)_x + offest;
			y += (int)_y + offest;

			_canvas.DrawString(Content, Font, new SolidBrush(TextColor), new RectangleF(x, y, tSize.Width, tSize.Height));
		}
	}

	public interface IGameState
	{
		void Update(int elapseMs);

		void Render();
	}

	public class MainState : IGameState
	{
		private Level _level;

		public event Action GameOverEvent;

		public MainState()
		{
			Level.CreateLevel();
			_level = Level.Instance;
		}

		public void Update(int elapseMs)
		{
			if (_level.IsGameOver)
			{
				if (GameOverEvent != null)
					GameOverEvent();
			}
			else
				_level.Update(elapseMs);
		}

		public void Render()
		{
			if (!_level.IsGameOver)
			{
				_level.Render();
				RenderManager.Instance.Render();
			}
		}
	}

	public class StartState : IGameState
	{
		private Sky _sky;
		private Forrest _forrest;
		private Mud _mud;
		private TextButton _startButton;
		private TextButton _exitButton;

		public event Action StartGameEvent;
		public event Action ExitGameEvent;

		public StartState()
		{
			var c = new Canvas(0, 0, 800, 430);
			_sky = new Sky(c);
			_forrest = new Forrest(new Canvas(0, 150, 800, 130));
			_mud = new Mud(new Canvas(0, 280, 800, 150));
			_startButton = new TextButton(c);
			_exitButton = new TextButton(c);

			_startButton.X = 300;
			_startButton.Y = 130;
			_startButton.Width = 200;
			_startButton.Height = 100;
			_startButton.Content = "Start";

			_startButton.Clicked += () => {
				if (StartGameEvent != null)
					StartGameEvent();
			};

			_exitButton.X = 300;
			_exitButton.Y = 260;
			_exitButton.Width = 200;
			_exitButton.Height = 100;
			_exitButton.Content = "Exit";

			_exitButton.Clicked += () =>
			{
				if (ExitGameEvent != null)
					ExitGameEvent();
			};
		}

		public void Update(int elapseMs)
		{
			_sky.Update(elapseMs);
		}

		public void Render()
		{
			_sky.Render();
			_mud.Render();
			_forrest.Render();
			_startButton.Render();
			_exitButton.Render();

			RenderManager.Instance.Render();
		}

		private TextButton _lastPressedButton = null;

		public void MouseDown(int x, int y)
		{
			var btn = FindButton(x, y);
			if (btn != null)
				btn.OnMouseDown();

			_lastPressedButton = btn;
		}

		public void MouseUp(int x, int y)
		{
			if (_lastPressedButton != null)
				_lastPressedButton.OnMouseUp();
		}

		private TextButton FindButton(int x, int y)
		{
			if (x > _startButton.X && x < _startButton.X + _startButton.Width &&
				y > _startButton.Y && y < _startButton.Y + _startButton.Height)
			{
				return _startButton;
			}
			else if (x > _exitButton.X && x < _exitButton.X + _exitButton.Width &&
					 y > _exitButton.Y && y < _exitButton.Y + _exitButton.Height)
			{
				return _exitButton;
			}
			else
				return null;
		}
	}
}
